﻿using System;
using System.Reflection;
using System.Runtime.CompilerServices;

namespace Extensions
{
    public static class ReflectionExtensions
    {
        public const string DataMember = "DataMemberAttribute";

        internal static string[] IgnoreAttributesNamed = new string[2]
        {
            "IgnoreDataMemberAttribute",
            "JsonIgnoreAttribute"
        };

        public static TypeCode GetTypeCode(this Type type)
        {
            return Type.GetTypeCode(type);
        }

        public static bool IsInstanceOf(this Type type, Type thisOrBaseType)
        {
            while (type != null)
            {
                if (type == thisOrBaseType)
                {
                    return true;
                }
                type = type.BaseType;
            }
            return false;
        }

        public static bool HasGenericType(this Type type)
        {
            while (type != null)
            {
                if (type.IsGenericType)
                {
                    return true;
                }
                type = type.BaseType;
            }
            return false;
        }

        public static Type FirstGenericType(this Type type)
        {
            while (type != null)
            {
                if (type.IsGenericType)
                {
                    return type;
                }
                type = type.BaseType;
            }
            return null;
        }

        public static Type GetTypeWithGenericTypeDefinitionOfAny(this Type type, params Type[] genericTypeDefinitions)
        {
            foreach (Type type2 in genericTypeDefinitions)
            {
                Type type3 = type.GetTypeWithGenericTypeDefinitionOf(type2);
                if (type3 == null && type == type2)
                {
                    type3 = type;
                }
                if (type3 != null)
                {
                    return type3;
                }
            }
            return null;
        }

        public static bool IsOrHasGenericInterfaceTypeOf(this Type type, Type genericTypeDefinition)
        {
            if (!(type.GetTypeWithGenericTypeDefinitionOf(genericTypeDefinition) != null))
            {
                return type == genericTypeDefinition;
            }
            return true;
        }

        public static Type GetTypeWithGenericTypeDefinitionOf(this Type type, Type genericTypeDefinition)
        {
            Type[] interfaces = type.GetInterfaces();
            foreach (Type type2 in interfaces)
            {
                if (type2.IsGenericType && type2.GetGenericTypeDefinition() == genericTypeDefinition)
                {
                    return type2;
                }
            }
            Type type3 = type.FirstGenericType();
            if (type3 != null && type3.GetGenericTypeDefinition() == genericTypeDefinition)
            {
                return type3;
            }
            return null;
        }

        public static Type GetTypeWithInterfaceOf(this Type type, Type interfaceType)
        {
            if (type == interfaceType)
            {
                return interfaceType;
            }
            Type[] interfaces = type.GetInterfaces();
            foreach (Type type2 in interfaces)
            {
                if (type2 == interfaceType)
                {
                    return type2;
                }
            }
            return null;
        }

        public static bool HasInterface(this Type type, Type interfaceType)
        {
            Type[] interfaces = type.GetInterfaces();
            for (int i = 0; i < interfaces.Length; i++)
            {
                if (interfaces[i] == interfaceType)
                {
                    return true;
                }
            }
            return false;
        }

        public static bool AllHaveInterfacesOfType(this Type assignableFromType, params Type[] types)
        {
            foreach (Type interfaceType in types)
            {
                if (assignableFromType.GetTypeWithInterfaceOf(interfaceType) == null)
                {
                    return false;
                }
            }
            return true;
        }

        public static bool IsNullableType(this Type type)
        {
            if (type.IsGenericType)
            {
                return type.GetGenericTypeDefinition() == typeof(Nullable<>);
            }
            return false;
        }

        public static TypeCode GetUnderlyingTypeCode(this Type type)
        {
            return (Nullable.GetUnderlyingType(type) ?? type).GetTypeCode();
        }

        public static bool IsIntegerType(this Type type)
        {
            if (type == null)
            {
                return false;
            }
            switch (type.GetTypeCode())
            {
                case TypeCode.SByte:
                case TypeCode.Byte:
                case TypeCode.Int16:
                case TypeCode.UInt16:
                case TypeCode.Int32:
                case TypeCode.UInt32:
                case TypeCode.Int64:
                case TypeCode.UInt64:
                    return true;

                case TypeCode.Object:
                    if (type.IsNullableType())
                    {
                        return Nullable.GetUnderlyingType(type).IsNumericType();
                    }
                    return false;

                default:
                    return false;
            }
        }

        public static bool IsNumericType(this Type type)
        {
            if (type == null)
            {
                return false;
            }
            if (type.IsEnum)
            {
                return true;
            }
            switch (type.GetTypeCode())
            {
                case TypeCode.SByte:
                case TypeCode.Byte:
                case TypeCode.Int16:
                case TypeCode.UInt16:
                case TypeCode.Int32:
                case TypeCode.UInt32:
                case TypeCode.Int64:
                case TypeCode.UInt64:
                case TypeCode.Single:
                case TypeCode.Double:
                case TypeCode.Decimal:
                    return true;

                case TypeCode.Object:
                    if (type.IsNullableType())
                    {
                        return Nullable.GetUnderlyingType(type).IsNumericType();
                    }
                    return false;

                default:
                    return false;
            }
        }

        public static bool IsRealNumberType(this Type type)
        {
            if (type == null)
            {
                return false;
            }
            switch (type.GetTypeCode())
            {
                case TypeCode.Single:
                case TypeCode.Double:
                case TypeCode.Decimal:
                    return true;

                case TypeCode.Object:
                    if (type.IsNullableType())
                    {
                        return Nullable.GetUnderlyingType(type).IsNumericType();
                    }
                    return false;

                default:
                    return false;
            }
        }

        public static Type GetTypeWithGenericInterfaceOf(this Type type, Type genericInterfaceType)
        {
            Type[] interfaces = type.GetInterfaces();
            foreach (Type type2 in interfaces)
            {
                if (type2.IsGenericType && type2.GetGenericTypeDefinition() == genericInterfaceType)
                {
                    return type2;
                }
            }
            if (!type.IsGenericType)
            {
                return null;
            }
            Type type3 = type.FirstGenericType();
            if (!(type3.GetGenericTypeDefinition() == genericInterfaceType))
            {
                return null;
            }
            return type3;
        }

        public static bool HasAnyTypeDefinitionsOf(this Type genericType, params Type[] theseGenericTypes)
        {
            if (!genericType.IsGenericType)
            {
                return false;
            }
            Type genericTypeDefinition = genericType.GetGenericTypeDefinition();
            foreach (Type right in theseGenericTypes)
            {
                if (genericTypeDefinition == right)
                {
                    return true;
                }
            }
            return false;
        }

        public static Type[] GetGenericArgumentsIfBothHaveSameGenericDefinitionTypeAndArguments(this Type assignableFromType, Type typeA, Type typeB)
        {
            Type typeWithGenericInterfaceOf = typeA.GetTypeWithGenericInterfaceOf(assignableFromType);
            if (typeWithGenericInterfaceOf == null)
            {
                return null;
            }
            Type typeWithGenericInterfaceOf2 = typeB.GetTypeWithGenericInterfaceOf(assignableFromType);
            if (typeWithGenericInterfaceOf2 == null)
            {
                return null;
            }
            Type[] genericArguments = typeWithGenericInterfaceOf.GetGenericArguments();
            Type[] genericArguments2 = typeWithGenericInterfaceOf2.GetGenericArguments();
            if (genericArguments.Length != genericArguments2.Length)
            {
                return null;
            }
            for (int i = 0; i < genericArguments2.Length; i++)
            {
                if (genericArguments[i] != genericArguments2[i])
                {
                    return null;
                }
            }
            return genericArguments;
        }

        public static bool AreAllStringOrValueTypes(params Type[] types)
        {
            foreach (Type type in types)
            {
                if (!(type == typeof(string)) && !type.IsValueType)
                {
                    return false;
                }
            }
            return true;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static object CreateInstance(this Type type)
        {
            if (type == null)
            {
                return null;
            }
            return Activator.CreateInstance(type);
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static T CreateInstance<T>(this Type type)
        {
            if (type == null)
            {
                return default(T);
            }
            return (T)Activator.CreateInstance(type);
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static Module GetModule(this Type type)
        {
            if (type == null)
            {
                return null;
            }
            return type.Module;
        }
    }
}